﻿''' <summary>
''' Represents a visible display surface with a border, z-order, and autoscrolling text content.
''' Windows can be parented to other windows and all open windows are drawn sorted by their ZOrder, 
''' according to ownership.  A window's bounds must fit within the console window bounds.
''' </summary>
Public Class Window
    Public Event FirstVisibleLineChanged As EventHandler
    Public Event GotFocus As EventHandler
    Public Event Hidden As EventHandler
    Public Event KeyProcessing As EventHandler(Of KeyProcessingEventArgs)
    Public Event LostFocus As EventHandler
    Public Event Shown As EventHandler
    Public Event Update As EventHandler

    Protected _Bounds As Rectangle
    Protected _ClientBounds As Rectangle
    Protected _FrameVisible As Boolean = True
    Protected _Visible As Boolean
    Protected atBottom As Boolean
    Protected firstLineVisible As Integer
    Protected needsErased As Boolean
    Protected previousFirstVisible As Integer
    Protected textCursorLeft As Integer
    Protected textCursorTop As Integer

    ''' <summary>
    ''' Creates a new Window the same size as the console window.
    ''' </summary>
    Public Sub New()
        Me.New(New Rectangle(0, 0, Console.WindowWidth, Console.WindowHeight))
    End Sub

    ''' <summary>
    ''' Creates a new Window at the specified location and of the specified size.
    ''' </summary>
    ''' <param name="x">The left-most side of the Window.</param>
    ''' <param name="y">The top-most side of the Window.</param>
    ''' <param name="width">The width of the Window.</param>
    ''' <param name="height">The height of the Window.</param>
    Public Sub New(x As Integer, y As Integer, width As Integer, height As Integer)
        Me.New(New Rectangle(x, y, width, height))
    End Sub

    ''' <summary>
    ''' Creates a new Window with the specified bounds.
    ''' </summary>
    ''' <param name="windowBounds">The bounds of the Window.</param>
    Public Sub New(windowBounds As Rectangle)
        Bounds = windowBounds
        openWindows.Add(Me)
    End Sub

    Protected Overridable Function GetBackcolor(x As Integer, y As Integer) As ConsoleColor
        Return BackColor
    End Function

    Protected Overridable Function GetForecolor(x As Integer, y As Integer) As ConsoleColor
        Return ForeColor
    End Function

    Protected Overridable Sub OffsetByParents(ByRef bounds As Rectangle)
        Dim p As Window = Parent
        While p IsNot Nothing
            bounds.Left += p.Bounds.Left
            bounds.Top += p.Bounds.Top
            p = p.Parent
        End While
    End Sub

    Protected Overridable Sub OffsetByParents(ByRef x As Integer, ByRef y As Integer)
        Dim p As Window = Parent
        While p IsNot Nothing
            x += p.Bounds.Left
            y += p.Bounds.Top
            p = p.Parent
        End While
    End Sub

    Protected Overridable Sub OnFirstVisibleLineChanged(e As EventArgs)
        RaiseEvent FirstVisibleLineChanged(Me, e)
    End Sub

    Protected Overridable Sub OnGotFocus(e As EventArgs)
        RaiseEvent GotFocus(Me, e)
    End Sub

    Protected Overridable Sub OnHidden(e As EventArgs)
        RaiseEvent Hidden(Me, e)
    End Sub

    Protected Overridable Sub OnLostFocus(e As EventArgs)
        RaiseEvent LostFocus(Me, e)
    End Sub

    Protected Overridable Sub OnPostDraw()
    End Sub

    Protected Overridable Sub OnShown(e As EventArgs)
        RaiseEvent Shown(Me, e)
    End Sub

    Protected Overridable Sub OnProcessKey(e As KeyProcessingEventArgs)
        RaiseEvent KeyProcessing(Me, e)
    End Sub

    Protected Overridable Sub OnUpdate(e As EventArgs)
        RaiseEvent Update(Me, e)
    End Sub

    Protected Overridable Sub ProcessKey(key As ConsoleKeyInfo)
        Dim args = New KeyProcessingEventArgs(key)
        OnProcessKey(args)
        If Not args.SuppressDefaultProcessing Then
            Select Case key.Key
                Case ConsoleKey.PageUp
                    If InvertTextDirection Then
                        ScrollDown()
                    Else
                        ScrollUp()
                    End If
                Case ConsoleKey.PageDown
                    If InvertTextDirection Then
                        ScrollUp()
                    Else
                        ScrollDown()
                    End If
                Case ConsoleKey.UpArrow
                    If InvertTextDirection Then
                        ScrollDown()
                    Else
                        ScrollUp()
                    End If
                Case ConsoleKey.DownArrow
                    If InvertTextDirection Then
                        ScrollUp()
                    Else
                        ScrollDown()
                    End If
                Case ConsoleKey.Escape
                    Hide()
                Case ConsoleKey.Tab
                    NextWindow()
            End Select
        End If
    End Sub

    Protected Friend Overridable Sub SetParent(owner As Window)
        _Parent = owner
    End Sub

    Protected Overridable Sub UpdateInternalBounds()
        If FrameVisible Then
            _ClientBounds = New Rectangle(_Bounds.Left + 1, _Bounds.Top + 1, _Bounds.Width - 2, _Bounds.Height - 2)
        Else
            _ClientBounds = _Bounds
        End If
        If Content.LineCount > ClientBounds.Height Then
            If FrameVisible Then
                _ClientBounds.Width = _Bounds.Width - 3
            Else
                _ClientBounds.Width = _Bounds.Width - 1
            End If
        End If
        Content._WindowClientBounds = _ClientBounds
    End Sub

End Class
